This page shows features that may have poor volume-rendering performance on slow devices.¶

Using anti-aliasing, high-DPI, and reflective volume rendering modes all impact performance.¶

This example is based off of https://niivue.com/demos/features/vox.tradeoffs.html.

In [1]:
from pathlib import Path

from ipyniivue import download_dataset

BASE_API_URL = "https://niivue.com/demos/images/"
DATA_FOLDER = Path("images")

# Download data for example
download_dataset(
    BASE_API_URL,
    DATA_FOLDER,
    files=[
        "mni152.nii.gz",
        "aal.nii.gz",
        "stats.nv_demo_mskd.nii.gz",
        "aal.json",
    ],
)
mni152.nii.gz already exists.
aal.nii.gz already exists.
Downloading stats.nv_demo_mskd.nii.gz...
aal.json already exists.
Dataset downloaded successfully to images.
In [2]:
import json

import ipywidgets as widgets

from ipyniivue import NiiVue
from ipyniivue.constants import ShowRender

# Create location status bar

status_bar = widgets.Label(value="")

# Create extra widgets

aa_check = widgets.Checkbox(value=True, description="Anti Alias")
dpi_check = widgets.Checkbox(value=True, description="High DPI")
render_mode = widgets.Dropdown(
    options=[
        ("slices", "-1"),
        ("matte", "0"),
        ("low", "0.3"),
        ("medium", "0.6"),
        ("high", "1.0"),
    ],
    value="0.6",
    description="Render Mode",
)
atlas_slider = widgets.IntSlider(
    value=64, min=0, max=255, description="Atlas", orientation="horizontal"
)
stat_slider = widgets.IntSlider(
    value=255, min=0, max=255, description="Statistics", orientation="horizontal"
)

# Create niivue instance

with open(DATA_FOLDER / "aal.json") as f:
    cmap = json.load(f)

nv = None  # Global variable to hold the NiiVue instance


def create_nv(anti_alias):
    """Create new NiiVue instance."""
    global nv

    if nv:
        # Don't forget to dispose of the previous nv if it exists
        nv.close()

    nv = NiiVue(
        back_color=(1, 1, 1, 1),
        show_3d_crosshair=True,
        is_anti_alias=anti_alias,
    )

    @nv.on_location_change
    def handle_location_change(location):
        """Display location."""
        status_bar.value = location["string"]

    nv.opts.multiplanar_show_render = ShowRender.ALWAYS
    nv.opts.is_colorbar = True

    volume_list = [
        {"path": DATA_FOLDER / "mni152.nii.gz"},
        {"path": DATA_FOLDER / "aal.nii.gz"},
        {
            "path": DATA_FOLDER / "stats.nv_demo_mskd.nii.gz",
            "colormap": "warm",
            "colormap_negative": "winter",
            "frame_4d": 1,
            "cal_min": 3.3641,
            "cal_max": 6,
        },
    ]
    nv.load_volumes(volume_list)

    # Hide colorbars for volumes 0 and 1
    nv.volumes[0].colorbar_visible = False
    nv.volumes[1].colorbar_visible = False
    nv.volumes[1].set_colormap_label(cmap)

    return nv


# Create initial nv
nv = create_nv(aa_check.value)

# Define event handlers


def on_dpi_check_change(change):
    """Set high resolution capable."""
    nv.set_high_resolution_capable(change["new"])


def on_render_mode_change(change):
    """Set volume render illumination."""
    nv.set_volume_render_illumination(float(change["new"]))


def on_atlas_slider_change(change):
    """Set 2nd volume opacity."""
    nv.volumes[1].opacity = change["new"] / 255.0


def on_stat_slider_change(change):
    """Set 3rd volume opacity."""
    nv.volumes[2].opacity = change["new"] / 255.0


def on_aa_check_change(change):
    """Recreate nv with new anti_alias setting and re-apply settings."""
    anti_alias = change["new"]
    # Create new nv
    new_nv = create_nv(anti_alias)

    # Update the UI container
    ui_container.children = (
        widgets.HBox([aa_check, dpi_check, render_mode]),
        widgets.HBox([atlas_slider, stat_slider]),
        new_nv,
        status_bar,
    )

    # Re-apply settings, except for antialias, because doing
    # so would result in an endless loop here
    on_dpi_check_change({"new": dpi_check.value})
    on_atlas_slider_change({"new": atlas_slider.value})
    on_stat_slider_change({"new": stat_slider.value})


# Set up observers

dpi_check.observe(on_dpi_check_change, names="value")
render_mode.observe(on_render_mode_change, names="value")
atlas_slider.observe(on_atlas_slider_change, names="value")
stat_slider.observe(on_stat_slider_change, names="value")
aa_check.observe(on_aa_check_change, names="value")

# Apply initial settings

# Except for antialias, because doing so would result in an endless loop here
# Also, not doing on_render_mode_change because canvas has not been attached yet
# so gl does not exist yet.
on_dpi_check_change({"new": dpi_check.value})
on_atlas_slider_change({"new": atlas_slider.value})
on_stat_slider_change({"new": stat_slider.value})

# Display all

ui_container = widgets.VBox(
    [
        widgets.HBox([aa_check, dpi_check, render_mode]),
        widgets.HBox([atlas_slider, stat_slider]),
        nv,
        status_bar,
    ]
)

display(ui_container)